using System;
using System.Collections.Generic;
using System.Text;
using Zsa.Emulation.Render;
using Zsa.Emulation.Config;
using System.Windows.Forms;
using System.Drawing;
using Zsa.Emulation.Util;
using System.Diagnostics;

namespace Zsa.Emulation
{
    /// <summary>
    /// Hosts the nes machine and render.
    /// </summary>
    public class NesMachineHost 
    {
        #region Fields

        /// <summary>
        /// Machine event.
        /// </summary>
        private event NesHostEvent _MachineEvent;

        /// <summary>
        /// The nes machine.
        /// </summary>
        private readonly NesMachine _Machine;
        /// <summary>
        /// The render controller.
        /// </summary>
        private Render.NesMachineRender _MachineRender;
        /// <summary>
        /// The configuration for the machine.
        /// </summary>
        private NesConfigurationProfile _Configuration;
        /// <summary>
        /// The machine host state.
        /// </summary>
        private MachineHostState _MachineState;

        /// <summary>
        /// The current frame.
        /// </summary>
        private int _Frame = 0;
        /// <summary>
        /// The current ticks elapsed.
        /// </summary>
        private double _Time = 0;
        /// <summary>
        /// Used to count fps.
        /// </summary>
        private Stopwatch _FpsWatch = new Stopwatch();
        private Control _hostControl;
        #endregion Fields

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="T:NesMachineHost"/> class.
        /// </summary>
        internal NesMachineHost()
            : this(NesConfigurationManager.Default, null, MachineHostFlags.Default)
        {
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="T:NesMachineHost"/> class.
        /// </summary>
        /// <param name="profile">The profile.</param>
        internal NesMachineHost(NesConfigurationProfile profile)
            : this(profile, null, MachineHostFlags.Default)
        {
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="T:NesMachineHost"/> class.
        /// </summary>
        /// <param name="surfaceControl">The surface control.</param>
        internal NesMachineHost(Control hostControl)
            : this(NesConfigurationManager.Default, hostControl, MachineHostFlags.Default)
        {
        }
        
        /// <summary>
        /// Initializes a new instance of the <see cref="T:NesMachineHost"/> class.
        /// </summary>
        internal NesMachineHost(NesConfigurationProfile profile,
            Control hostControl, MachineHostFlags flags)
        {
            if (profile == null)
            {
                throw new ArgumentNullException(
                    "profile",
                    "The configuration profile cannot be null !"
                    );
            }
            _Configuration = profile;
            _MachineRender = new NesMachineRender(this, _Configuration);
            // If the host is not null then try to prepare the surface control.
            if (hostControl != null)
            {
                _hostControl = SetControlHost(hostControl);
            }
            // Create the machine.
            _Machine = new NesMachine(profile.MachineSettings);
            
        }

        #endregion Constructor

        #region Methods

        /// <summary>
        /// Starts this instance.
        /// </summary>
        public void Start()
        {
            _Machine.Start();
            _MachineRender.Init();
        }

        /// <summary>
        /// Sets the control host.
        /// </summary>
        /// <param name="hostControl">The host control.</param>
        public Control SetControlHost(Control hostControl)
        {
            return _MachineRender.SetControlHost(hostControl);
        }

        /// <summary>
        /// Loads the cartridge.
        /// </summary>
        /// <param name="cartFileInfo">The cart file info.</param>
        public void LoadCartridge(Cartridge.CartridgeFileInfo cartFileInfo)
        {
            // Load the cart.
            if (_Machine.Status == MachineState.NeverStarted)
            {
                _Machine.Reset();
            }
            _Machine.CartridgeSlot.LoadCart(cartFileInfo);
        }
        
        /// <summary>
        /// Runs this instance.
        /// </summary>
        internal float Run()
        {
            // Get the number of frame to skip.
            int _FrameSkipTemp = 0;
            // Reset the stop watch.
            _FpsWatch.Reset();
            _FpsWatch.Start();
            // Executes enough cpu cycles to render the specified frames.
            _Machine.Run(1+_FrameSkipTemp);
            // Renders a video, sound, etc,
            _MachineRender.Render();
            // Stop the watch.
            _FpsWatch.Stop();
            // Add the number of frames rendered.
            _Frame += (1 + _FrameSkipTemp);
            // Add the amount of elapse time.
            _Time += _FpsWatch.Elapsed.TotalMilliseconds;

            return 1;
        }

      

        /// <summary>
        /// Raises the event.
        /// </summary>
        /// <param name="eventType">Type of the event.</param>
        private void RaiseEvent(MachineHostEventType eventType)
        {
            if (_MachineEvent != null)
            {
                _MachineEvent(this, new NesMachineEventArgs(eventType, this));
            }
        }

        public void Close()
        {
            try
            {
                _Machine.Shutdown();
                _MachineRender.Shutdown();
            }
            catch( Exception excep)
            {
                Console.WriteLine(excep);
            }
        }

        /// <summary>
        /// Disposes this instance.
        /// </summary>
        internal void Dispose()
        {
        }
        #endregion Methods

        #region Properties

        /// <summary>
        /// Gets the machine.
        /// </summary>
        /// <value>The machine.</value>
        public NesMachine Machine
        {
            get
            {
                return _Machine;
            }
        }

        /// <summary>
        /// Gets the machine render.
        /// </summary>
        /// <value>The machine render.</value>
        public Render.NesMachineRender MachineRender
        {
            get
            {
                return _MachineRender;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance can render.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance can render; otherwise, <c>false</c>.
        /// </value>
        public bool CanRender
        {
            get
            {
                //return _RenderController 
                return false;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance has cart.
        /// </summary>
        /// <value><c>true</c> if this instance has cart; otherwise, <c>false</c>.</value>
        public bool HasCart
        {
            get
            {
                return _Machine.CartridgeSlot.IsEmpty;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance has surface.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has surface; otherwise, <c>false</c>.
        /// </value>
        public bool HasSurface
        {
            get
            {
                return _MachineRender.HasSurface;
            }
        }

        /// <summary>
        /// Gets the state of the host.
        /// </summary>
        /// <value>The state of the host.</value>
        public MachineHostState HostState
        {
            get
            {
                return _MachineState;
            }
        }

        /// <summary>
        /// Gets the current cart.
        /// </summary>
        /// <value>The current cart.</value>
        public Cartridge.CartridgeFileInfo CurrentCart
        {
            get
            {
                return _Machine.CartridgeSlot.CurrentCart.FileInfo;
            }
        }

        /// <summary>
        /// Gets the FPS.
        /// </summary>
        /// <value>The FPS.</value>
        public float Fps
        {
            get
            {
                if (_Frame == 0 || _Time == 0)
                {
                    return 0;
                }
                double _Ticks = (double)(_Time / _Frame);
                if (_Frame > 200)
                {
                    _Frame = 0;
                    _Time = 0;
                }
                return (float)Math.Round(1000D / _Ticks, 3);

            }

        }
        #endregion Properties



    }
}
